package com.bigfans.framework.aspect;

import java.lang.reflect.Method;
import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.bigfans.framework.cache.CacheEntry;
import com.bigfans.framework.cache.CacheEvict;
import com.bigfans.framework.cache.CacheKey;
import com.bigfans.framework.cache.CacheKeyGenerator;
import com.bigfans.framework.cache.CacheList;
import com.bigfans.framework.cache.CacheProvider;
import com.bigfans.framework.cache.Cacheable;
import com.bigfans.framework.cache.MethodCache;
import com.bigfans.framework.model.AbstractModel;
import com.bigfans.framework.plugins.PluginManager;
import com.bigfans.framework.utils.CollectionUtils;
import com.bigfans.framework.utils.StringHelper;



/**
 * 
 * @Title: 
 * @Description: 缓存插件,拦截Cacheable,CacheList,CacheEvict等注解
 * @author lichong 
 * @date 2016年1月17日 上午9:46:14 
 * @version V1.0
 */
@Aspect
@Component
public class CacheAspect {
	/* 缓存返回的结果 */
	private static final String GET = "@annotation(cacheable)";
	private static final String EVICT = "@annotation(cacheEvict)";
	/* 取出集合中的每一条记录进行单独缓存 */
	private static final String LIST = "@annotation(cacheList)";
	
	private static final String METHOD_CACHE = "@annotation(methodCache)";

	@Autowired
	private CacheProvider cacheProvider;

	public CacheProvider getCacheProvider() {
		return cacheProvider;
	}

	/**
	 * @annotation(cacheable) 中的 "cacheable" 和参数中的名称相对应，
	 * aspect会根据参数对应的类型去拦截标注了Cacheable的方法
	 * @param call
	 * @param cacheable
	 * @return
	 * @throws Throwable
	 */
	@Around(value = GET)
	public Object get(ProceedingJoinPoint call, Cacheable cacheable) throws Throwable {
		if(getCacheProvider() == null){
			return call.proceed();
		}
		Object[] args = call.getArgs();
		// 获取参数中的id，如果id为空，那么不进行缓存
		for (Object object : args) {
			if(object instanceof AbstractModel){
				String id = ((AbstractModel) object).getId();
				if(StringHelper.isEmpty(id)){
					return call.proceed();
				}
			}
		}
		// 根据id生成缓存的key
		Class<?> targetClass = call.getTarget().getClass();
		MethodSignature methodSignature = (MethodSignature)call.getSignature();
		Class<? extends CacheKeyGenerator> ckgClass = cacheable.keyGenerator();
		String key = ckgClass.newInstance().generate(targetClass.getSimpleName(), methodSignature.getMethod(), args);
		
		// 存储到缓存
		CacheKey cacheKey = new CacheKey(key);
		CacheEntry cachedEntry = getCacheProvider().get(cacheKey);
		Object result = null ;
		if(cachedEntry == null){
			result = call.proceed();
			if(result != null){
				cachedEntry = new CacheEntry(cacheKey , result);
				getCacheProvider().put(cachedEntry);
			}
		}else{
			result = cachedEntry.getValue();
		}
		return result;
	}
	
	@Around(EVICT)
	public Object evict(ProceedingJoinPoint call , CacheEvict cacheEvict) throws Throwable {
		if(getCacheProvider() == null){
			return call.proceed();
		}
		Integer resultCount = (Integer) call.proceed();
		if(resultCount == 0){
			return 0;
		}
		Method me = ((MethodSignature) call.getSignature()).getMethod();
		Object[] args = call.getArgs();
		Class<?> targetClass = call.getTarget().getClass();
		Class<? extends CacheKeyGenerator> ckgClass = cacheEvict.keyGenerator();
		String key = ckgClass.newInstance().generate(targetClass, me , args);
		CacheKey cacheKey = new CacheKey(key);
		CacheEntry cachedEntry = getCacheProvider().get(cacheKey);
		if(cachedEntry != null){
			getCacheProvider().evict(cacheKey);
		}
		return resultCount;
	}
	
	@Around(LIST)
	public Object list(ProceedingJoinPoint call , CacheList cacheList) throws Throwable{
		if(getCacheProvider() == null){
			return call.proceed();
		}
		Object result = call.proceed();
		if(result == null){
			return null;
		}
		List<AbstractModel> resultList = (List<AbstractModel>) result;
		if(CollectionUtils.isEmpty(resultList)){
			return resultList;
		}
		Method me = ((MethodSignature) call.getSignature()).getMethod();
		Object[] args = call.getArgs();
		Class<?> targetClass = call.getTarget().getClass();
		Class<? extends CacheKeyGenerator> ckgClass = cacheList.keyGenerator();
		
		for (AbstractModel model : resultList) {
			String key = ckgClass.newInstance().generate(targetClass, me , args);
			CacheKey cacheKey = new CacheKey(key);
			CacheEntry cachedEntry = getCacheProvider().get(cacheKey);
			// 从缓存中取出model，如果找到则先移除再插入最新的
			if(cachedEntry != null){
				getCacheProvider().evict(cacheKey);
			}
			cachedEntry = new CacheEntry(cacheKey , model);
			getCacheProvider().put(cachedEntry);
		}
		return resultList;
	}
	
	@Around(METHOD_CACHE)
	public Object methodCache(ProceedingJoinPoint call , MethodCache methodCache) throws Throwable{
		if(getCacheProvider() == null){
			return call.proceed();
		}
		String key = null;
		if(StringHelper.isNotEmpty(methodCache.key())){
			key = methodCache.key();
		} else {
			// 根据id生成缓存的key
			Object[] args = call.getArgs();
			Class<?> targetClass = call.getTarget().getClass();
			Method me = ((MethodSignature) call.getSignature()).getMethod();
			Class<? extends CacheKeyGenerator> ckgClass = methodCache.keyGenerator();
			key = ckgClass.newInstance().generate(targetClass, me, args);
		}
		
		// 先从缓存中获取方法的缓存结果
		CacheKey cacheKey = new CacheKey(key);
		CacheEntry cachedEntry = getCacheProvider().get(cacheKey);
		if(cachedEntry != null){
			return cachedEntry.getValue();
		}
		// 如果缓存中没有，执行方法，将返回值放入到缓存中
		Object result = call.proceed();
		if(result == null){
			return null;
		}
		cachedEntry = new CacheEntry(cacheKey, result);
		getCacheProvider().put(cachedEntry);
		return result;
	}
}
